﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenExpressApp.Editors;
using System.Windows.Controls;
using System.Windows.Data;
using Csla.Wpf;
using System.Collections;
using System.ComponentModel;
using Csla.Core;
using OpenExpressApp.WPF.Controls;
using System.Diagnostics;
using OpenExpressApp.Module.WPF;
using Itenso.Windows.Input;
using OpenExpressApp.Module;
using OpenExpressApp.WPF.Command;
using OpenExpressApp.MetaModel;
using OpenExpressApp.Module.WPF.Controls;
using System.Windows.Media;
using System.Windows;

namespace OpenExpressApp.Module.WPF.Editors
{
    /// <summary>
    /// 列表选择属性编辑器
    /// </summary>
    public class LookupListPropertyEditor : WPFPropertyEditor
    {
        private GCslaDataProvider _dataProvider;

        private ComboDataGrid _cmbGrid;

        /// <summary>
        /// 数据源
        /// </summary>
        private List<object> _dataSource = new List<object>();

        public LookupListPropertyEditor(BusinessObjectPropertyInfo propertyInfo, IObjectView view)
            : base(propertyInfo, view) { }

        /// <summary>
        /// 清空下拉列表数据
        /// </summary>
        public void ClearDropDownData()
        {
            if (this._dataProvider != null)
            {
                this._dataProvider.ObjectInstance = null;
            }
        }

        #region 控件生成

        /// <summary>
        /// 生成等待控件
        /// </summary>
        /// <returns></returns>
        protected override FrameworkElement CreateLabelElement()
        {
            var panel = base.CreateLabelElement() as Panel;

            //生成busy控件
            var busy = new BusyAnimation
            {
                Width = 20,
            };

            //绑定TextBox到对象属性
            var bdIsRunning = new Binding("IsBusy");
            bdIsRunning.Source = this._dataProvider;
            bdIsRunning.BindsDirectlyToSource = true;
            busy.SetBinding(BusyAnimation.IsRunningProperty, bdIsRunning);

            panel.Children.Add(busy);

            return panel;
        }

        /// <summary>
        /// 生成下拉框控件
        /// </summary>
        /// <returns></returns>
        protected override FrameworkElement CreateEditingElement()
        {
            this.InitDataProvider();

            var boInfo = ApplicationModel.GetBusinessObjectInfo(this.PropertyInfo.LookupAttribute.LookupType);
            string lookupTitlePropertyName = boInfo.TitleInfo.Name;

            this._cmbGrid = new ComboDataGrid(this)
            {
                IsEditable = true,
                IsReadOnly = false,
                SelectedValuePath = DBConvention.FieldName_Id,
                DisplayMemberPath = lookupTitlePropertyName,
                Name = PropertyInfo.Name,
                StaysOpenOnEdit = true,
                IsTextSearchEnabled = false
            };
            this._cmbGrid.DropDownOpened += new EventHandler(cmbGrid_DropDownOpened);
            this._cmbGrid.SelectionChanged += new SelectionChangedEventHandler(cmbGrid_SelectionChanged);
            this._cmbGrid.PatternChanged += new EventHandler<ComboDataGrid.AutoCompleteArgs>(cmbGrid_PatternChanged);

            //设置_cb 的 binding
            //绑定TextBox到对象属性
            Binding textBinding = new Binding();
            this.PrepareBinding(textBinding);
            this._cmbGrid.SetBinding(ComboBox.TextProperty, textBinding);

            return this._cmbGrid;
        }

        protected override void PrepareBinding(Binding binding)
        {
            base.PrepareBinding(binding);

            var lookupAttr = this.PropertyInfo.LookupAttribute;
            Type lookupType = lookupAttr.LookupType;
            var boInfo = ApplicationModel.GetBusinessObjectInfo(lookupType);
            string lookupTitlePropertyName = boInfo.TitleInfo.Name;

            if (String.IsNullOrEmpty(lookupAttr.LookupPropertyName))
            {
                binding.Path = new PropertyPath(PropertyInfo.Name);
                binding.Mode = BindingMode.TwoWay;
            }
            else
            {
                binding.Path = new PropertyPath(lookupAttr.LookupPropertyName + "." + lookupTitlePropertyName);
                //设置绑定模式，不允许从Text修改到原对象（bug #18098）
                binding.Mode = BindingMode.OneWay;
            }
        } 

        #endregion

        /// <summary>
        /// 在设置值时，更新下拉框中的值
        /// </summary>
        protected override void OnPropertyValueChanged()
        {
            base.OnPropertyValueChanged();

            this.LocateCurrentObject();
        }

        /// <summary>
        /// 下拉控件打开时发生
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbGrid_DropDownOpened(object sender, EventArgs e)
        {
            if (this.IsReadOnly)
            {
                this._cmbGrid.IsDropDownOpen = false;
                return;
            }

            this.LoadData();

            if (string.IsNullOrEmpty(this._cmbGrid.Text) == false)
            {
                this.LocateCurrentObject();
            }
            else
            {
                // this.PropertyValue = null;
                this._cmbGrid.SelectedItem = null;
            }
        }

        public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

        /// <summary>
        /// 下拉控件选择某项时发生。
        /// 在这里设置CurrentObject相关的值。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            //确保只在LookupListPropertyEditorControl中触发一次
            if (e.OriginalSource != e.Source) return;

            this.SetValueBySelectedItem();

            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(this, e);
            }

            e.Handled = true;
        }

        #region Load Data

        /// <summary>
        /// 初始化数据提供器
        /// </summary>
        private void InitDataProvider()
        {
            //如果使用属性，则不需要使用CslaDataProvider
            if (string.IsNullOrEmpty(this.PropertyInfo.LookupAttribute.DataSourceProperty) == false) return;

            if (this._dataProvider == null)
            {
                this._dataProvider = new GCslaDataProvider();

                (this._dataProvider as ISupportInitialize).BeginInit();

                this._dataProvider.IsAsynchronous = true;
                this._dataProvider.ManageObjectLifetime = false;
                this._dataProvider.IsInitialLoadEnabled = false;

                var lookupType = this.PropertyInfo.LookupAttribute.LookupType;
                var lookupAssembly = lookupType.Assembly;
                var fullName = lookupType.FullName;

                //ObjectType
                this._dataProvider.ObjectType = lookupAssembly.GetType(fullName + "List");
                if (this._dataProvider.ObjectType == null)
                {
                    this._dataProvider.ObjectType = lookupAssembly.GetType(fullName + "s");
                }
                this._dataProvider.FactoryMethod = "GetList";

                (this._dataProvider as ISupportInitialize).EndInit();
            }

            this._dataProvider.DataChanged += dataProvider_DataChanged;
        }

        /// <summary>
        /// 加载数据
        /// 
        /// 如果已经初始化CslaDataProvider，则从里面获取数据。
        /// 否则表示
        /// </summary>
        public void LoadData()
        {
            this.LoadData(true);
        }

        /// <summary>
        /// 加载数据
        /// 
        /// 如果已经初始化CslaDataProvider，则从里面获取数据。
        /// 否则表示
        /// </summary>
        /// <param name="async">
        /// 是否使用异步模式
        /// </param>
        public void LoadData(bool async)
        {
            if (this._dataProvider != null)
            {
                //如果是每一次打开，则只需要获取数据即可。
                if (this._dataProvider.Data == null)
                {
                    if (async)
                    {
                        //异步获取数据，完成后发生事件：dataProvider_DataChanged
                        using (this._dataProvider.DeferRefresh()) { }
                    }
                    else
                    {
                        this._dataProvider.Refresh();
                    }
                }
            }
            else
            {
                this.LoadDataFromProperty();
            }
        }

        public void LocateFirstObject()
        {
            if (this._cmbGrid != null && this._cmbGrid.ItemsSource != null)
            {
                if (this._cmbGrid.ItemsSource.GetEnumerator().MoveNext())
                {
                    this._cmbGrid.SelectedIndex = 0;
                }
            }
        }

        /// <summary>
        /// 数据改变时，刷新控件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataProvider_DataChanged(object sender, EventArgs e)
        {
            var data = this._dataProvider.Data as IEnumerable;

            this._cmbGrid.ItemsSource = data;

            if (data != null)
            {
                foreach (var item in data)
                {
                    this._dataSource.Add(item);
                }
            }
            //this.LocateCurrentObject();
        }

        /// <summary>
        /// 从指定的属性中获取数据列表。
        /// </summary>
        private void LoadDataFromProperty()
        {
            //如果设定了DataSourceProperty，则从这个属性中获取下拉对象集合
            var lookupAttr = this.PropertyInfo.LookupAttribute;
            var dataSourceProperty = lookupAttr.DataSourceProperty;
            Debug.Assert(string.IsNullOrEmpty(dataSourceProperty) == false, "string.IsNullOrEmpty(dataSourceProperty) == false");

            //ItemsSource可能是懒加载属性，放在DropDownOpened可以防止初始化界面时就去加载数据
            var bindingExpression = this._cmbGrid.GetBindingExpression(ComboBox.ItemsSourceProperty);
            if (null == bindingExpression)
            {
                Binding itemsSourceBinding = new Binding(dataSourceProperty);
                this._cmbGrid.SetBinding(ComboBox.ItemsSourceProperty, itemsSourceBinding);
                bindingExpression = this._cmbGrid.GetBindingExpression(ComboBox.ItemsSourceProperty);
            }

            //更新控件
            bindingExpression.UpdateTarget();

            //如果有RootPID的值，则控件只显示Parent下面的子节点
            var rootPId = lookupAttr.RootPIdProperty;
            if (string.IsNullOrEmpty(rootPId) == false)
            {
                var rootPidValue = this.View.CurrentObject.GetPropertyValue(rootPId);
                this._cmbGrid.BindData(rootPidValue);
            }
        }

        #endregion

        /// <summary>
        /// 根据当前的值，找到并定位到当前对象
        /// </summary>
        private void LocateCurrentObject()
        {
            if (this._cmbGrid.Items.Count == 0) return;

            //找到需要选中的selectedItem
            object selectedItem = null;
            if (this.View.CurrentObject != null && this.PropertyInfo != null)
            {
                string selectedValuePath = this.PropertyInfo.LookupAttribute.SelectedValuePath;
                if (string.IsNullOrEmpty(selectedValuePath))
                {
                    selectedValuePath = DBConvention.FieldName_Id;
                }

                //根据PropertyValue找到对应的CurrentObject
                var propertyValue = this.PropertyValue;
                foreach (var item in this._cmbGrid.ItemsSource)
                {
                    var itemValue = item.GetPropertyValue(selectedValuePath);
                    if (object.Equals(propertyValue, itemValue))
                    {
                        selectedItem = item;
                        break;
                    }
                }
            }

            //定位CurrentObject
            this._cmbGrid.SelectionChanged -= cmbGrid_SelectionChanged;
            this._cmbGrid.SelectedItem = selectedItem;
            this._cmbGrid.SelectionChanged += cmbGrid_SelectionChanged;
        }

        /// <summary>
        /// 根据选中的对象设置CurrentObject相关的值。
        /// </summary>
        private void SetValueBySelectedItem()
        {
            var selecteItem = this._cmbGrid.SelectedItem as BusinessBase;
            if (selecteItem != null)
            {
                var lookupAttr = this.PropertyInfo.LookupAttribute;
                var lookupProperty = lookupAttr.LookupPropertyName;

                //设置View.CurrentObject指定的lookupProperty属性为选中的对象
                if (string.IsNullOrEmpty(lookupProperty) == false)
                {
                    var cur = this.View.CurrentObject as BusinessBase;
                    if (cur != null)
                    {
                        cur.SetPropertyValue(lookupProperty, selecteItem);
                    }
                }

                //根据SelectedValuePath指定的值，赋值给this.PropertyValue
                string selectedValuePath = lookupAttr.SelectedValuePath;
                if (string.IsNullOrEmpty(selectedValuePath))
                {
                    this.PropertyValue = selecteItem.GetPropertyValue(DBConvention.FieldName_Id);
                }
                else
                {
                    this.PropertyValue = selecteItem.GetPropertyValue(selectedValuePath);
                }
            }
        }

        #region AutoComplete

        private void cmbGrid_PatternChanged(object sender, ComboDataGrid.AutoCompleteArgs args)
        {
            //check
            //if (string.IsNullOrEmpty(args.Pattern))
            //    args.CancelBinding = true;
            //else
            args.DataSource = this.GetSearchItems(args.Pattern);
        }

        /// <summary>
        /// 在数据源中按输入的文字查找符合要求的项
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private IEnumerable GetSearchItems(string pattern)
        {
            if (string.IsNullOrEmpty(pattern))
            {
                return _dataSource;
            }
            else
            {
                List<object> items = new List<object>();
                foreach (var item in _dataSource)
                {
                    if (item.ToString().Contains(pattern))
                    {
                        items.Add(item);
                    }
                }
                return items;
            }
        }

        #endregion
    }
}